package com.cp.admin.core.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cp.admin.api.entity.Result;
import com.cp.core.support.StrKit;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.web3j.tx.Contract.GAS_LIMIT;
import static org.web3j.tx.ManagedTransaction.GAS_PRICE;

/**
 * @author cj
 * @create 2019-04-08 14:09
 **/
public class EthUtil {

    private static String nodeUrl = "https://mainnet.infura.io/v3/160bd8bfb0e14299bd3dbd91aff7b6eb";

    public static Result signEthTokenTransactionAndPush(String privateKey, String toAddress, BigDecimal amount,
                                                        String contractAddress, BigInteger count, int Precision) {
        Result result = new Result();
        try {

            Web3j web3j = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
            //解密
            String PlaintextPr = Security.decrypt(privateKey);

            BigInteger bigInteger = new BigInteger(PlaintextPr, 16);

            ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);

            //钱包凭证
            Credentials credentials = Credentials.create(ecKeyPair);
            //获取gasPrice
            EthGasPrice ethGasPrice = web3j.ethGasPrice().send();

            BigInteger gasPrice = ethGasPrice.getGasPrice();

            BigInteger GAS_LIMIT = BigInteger.valueOf(48000l);

            //计算精度
            BigDecimal weiValue = amount.multiply(BigDecimal.TEN.pow(Precision));

            BigInteger sendValue = weiValue.toBigIntegerExact();

            Function function = new Function("transfer", Arrays.asList(new Address(toAddress), new Uint256(sendValue)),
                    Collections.emptyList());
            String encodedFunction = FunctionEncoder.encode(function);

            //生成原始交易
            RawTransaction rawTransaction = RawTransaction.createTransaction(count, gasPrice, GAS_LIMIT,
                    contractAddress, encodedFunction);
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            //获取原始交易hex
            String hexValue = Numeric.toHexString(signedMessage);
            Result pushTx = pushEthTransaction(hexValue);
            if (pushTx.getCode() != 0) {
                result.setCode(-1);
                result.setMessage("发送失败："+pushTx.getMessage());
                return result;
            }
            result.setData(pushTx.getData());

            return result;
        }catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 发起一笔交易
     * 使用以太钱包文件发送以太币给其他人，不能设置nonce：（推荐）
     *
     * @param privateKey
     */
    public static Result transfer(String toAddress, BigDecimal value, String privateKey) {
        Result result = new Result();
        Web3j web3j = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
        //转账者私钥
        //解密
        String PlaintextPr = Security.decrypt(privateKey);

        BigInteger bigInteger = new BigInteger(PlaintextPr, 16);
        ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
        Credentials credentials = Credentials.create(ecKeyPair);
        TransactionReceipt transactionReceipt = null;
        try {
            transactionReceipt = Transfer.sendFunds(
                    web3j, credentials, toAddress,
                    value, Convert.Unit.ETHER).sendAsync().get();
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("发送失败："+e.getMessage());
        }
        String transactionHash = transactionReceipt.getTransactionHash();
        System.out.println("transfer: " + transactionHash);

        result.setData(transactionHash);
        return result;
    }

    public static Result pushEthTransaction(String hexValue) {
        Result result = new Result();
        try{
            Web3j web3j = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
            //发送原始交易
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
            //获取交易hash
            String transactionHash = ethSendTransaction.getTransactionHash();

            if (ethSendTransaction.getError() != null) {
                result.setCode(ethSendTransaction.getError().getCode());
                result.setMessage(ethSendTransaction.getError().getMessage());
            }
            result.setData(transactionHash);

        }catch (Exception e){
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("发送失败："+e.getMessage());
        }

        return result;

    }

    public static Result tokenBalance(String contractAddress, String address) {
        Result result = new Result();

        Web3j web3 = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
        try {

            Credentials credentials = Credentials.create(Keys.createEcKeyPair());

            HumanStandardToken humanStandardToken = HumanStandardToken.load(
                    contractAddress.toLowerCase(), web3, credentials, GAS_PRICE, GAS_LIMIT);
            BigInteger ba = humanStandardToken.balanceOf(address).send();

            BigDecimal balance = new BigDecimal(ba);
            result.setData(balance);
        }catch (Exception e) {
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("获取余额失败："+e.getMessage());
        }

        return result;
    }
    public static Result getBalance(String address) {
        Result result = new Result();
        Web3j web3j = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
        EthGetBalance ethGetBlance = null;
        try {
            ethGetBlance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
        } catch (IOException e) {
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("获取余额失败："+e.getMessage());
        }
        // 格式转换 WEI(币种单位) --> ETHER
        String balance = Convert.fromWei(new BigDecimal(ethGetBlance.getBalance()), Convert.Unit.ETHER).toPlainString();

        result.setData(balance);
        return  result;
    }
    public static Result getCount(String address) {
        Result result = new Result();

        Web3j web3 = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
        try {
            EthGetTransactionCount ethGetTransactionCount = web3.ethGetTransactionCount(
                    address, DefaultBlockParameterName.PENDING).sendAsync().get();
            BigInteger count = ethGetTransactionCount.getTransactionCount();
            result.setData(count);
        }catch (Exception e) {
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("获取失败："+e.getMessage());
        }

        return result;
    }

    public static Result getTxConfirmation(String txid){
        Result result = new Result();
        if(StrKit.isEmpty(txid)){
            result.setCode(-1);
            result.setMessage("缺少txid");
            return result;
        }

        Web3j web3 = Web3j.build(new org.web3j.protocol.http.HttpService(nodeUrl));
        try {
            EthTransaction ethTransaction = web3.ethGetTransactionByHash(txid).send();
            EthBlockNumber ethBlockNumber = web3.ethBlockNumber().send();
            BigInteger blockNumber = ethBlockNumber.getBlockNumber();
            BigInteger txBlockNumber = ethTransaction.getTransaction().get().getBlockNumber();
            BigInteger confirmation = blockNumber.subtract(txBlockNumber).add(BigInteger.ONE);
            result.setData(confirmation);
        }catch (Exception e) {
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("获取失败："+e.getMessage());
        }

        return result;
    }

    public static Result getTxConfirmationFromScan(String txid){
        Result result = new Result();
        if(StrKit.isEmpty(txid)){
            result.setCode(-1);
            result.setMessage("缺少txid");
            return result;
        }
        try {
            String ethUrl ="http://api.etherscan.io/api";
            //String hash = "0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1";
            String apikey = "A9DZGE832S7IT9IYR31AEQ1HFI29ZWF86K";
            Map<String, String> map = new HashMap();
            map.put("module","proxy");
            map.put("action","eth_getTransactionByHash");
            map.put("txhash",txid);
            map.put("apikey",apikey);
            String re = HttpClientUtils.postParameters(ethUrl, map);
            JSONObject jsonObject = JSON.parseObject(re).getJSONObject("result");

            String txBlockNum0x = jsonObject.getString("blockNumber").replaceAll("0x","");
            BigInteger txBlockNum = new BigInteger(txBlockNum0x,16);
            map = new HashMap();
            map.put("module","proxy");
            map.put("action","eth_blockNumber");
            map.put("apikey",apikey);
            String blockNumRe = HttpClientUtils.postParameters(ethUrl, map);
            String BRJ = JSON.parseObject(blockNumRe).getString("result").replaceAll("0x","");
            BigInteger blockNum = new BigInteger(BRJ,16);
            BigInteger confirmation = blockNum.subtract(txBlockNum).add(BigInteger.ONE);

            result.setData(confirmation);
        }catch (Exception e) {
            e.printStackTrace();
            result.setCode(-1);
            result.setMessage("获取失败："+e.getMessage());
        }

        return result;
    }
//
    public static void main(String[] args) throws Exception {
        String PlaintextPr = "CQddUQ1SUQJfVFRWAwcEAgQIA1MNXQVVUQQBAlMCVFIGUwNaBQQCAwUAV1VRVAUEAAEEDFZQDgRWBlYCVlRXAg@@";

        Result result = signEthTokenTransactionAndPush(PlaintextPr,"0x7263822020129f71901A1aAf135Ad465722E90De",BigDecimal.valueOf(100),"0xdac17f958d2ee523a2206206994597c13d831ec7",BigInteger.ZERO,6);
//Result result = transfer("0x7263822020129f71901A1aAf135Ad465722E90De",BigDecimal.valueOf(0.0135),PlaintextPr);
        System.out.println(result);
    }
}